#ifdef COUT_DEBUG
#include <iostream>
using namespace std;
#endif
#include <wx/sckstrm.h>

#include "wxcmdprot.h"
#include <iostream>
#include <sstream>
#include <queue>
#include <string>
using namespace std;


#define SOCKET_ID 1


//
// Socket events could be delivered after the socket is closed. Since
// we would like to reuse the socket, we have to ensure events for a
// previous connection are not interpreted as applying to the current
// connection.
//
// Fortunately, we are able to set a void* serial number ("ClientData")
// for the socket which will be copied into each event, allowing us to
// distinguish old events from current events.
//
// We increment the serial number (m_serial) every time we want to start
// ignoring old events. Events arriving with a non-current serial number
// are discarded.
//
class wxCmdlineProtocolSocketEventHandler : public wxEvtHandler
{
public:
	wxCmdlineProtocolSocketEventHandler(wxCmdlineProtocol& callback)
	    :
	    m_callback(callback),
	    m_serial(1)
	{
	}

	void OnSocketEvent(wxSocketEvent& event)
	{
	    if (event.GetClientData() == (void *)m_serial)
		m_callback.OnSocketEvent(event);
	}

	void incserial()
	{
	    ++m_serial;
	}

	int getserial()
	{
	    return m_serial;
	}

private:
	wxCmdlineProtocol	&m_callback;
	int			m_serial;

	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(wxCmdlineProtocolSocketEventHandler, wxEvtHandler)
  EVT_SOCKET(SOCKET_ID, wxCmdlineProtocolSocketEventHandler::OnSocketEvent)
END_EVENT_TABLE()


wxCmdlineProtocol::wxCmdlineProtocol()
    :
    m_host(wxT("")),
    m_service(wxT("")),
    m_port(0),
    m_user(wxT("")),
    m_password(wxT(""))
{
	m_pCmdlineProtocolSocketEventHandler =
	    new wxCmdlineProtocolSocketEventHandler(*this);
}

wxCmdlineProtocol::~wxCmdlineProtocol()
{
	delete m_pCmdlineProtocolSocketEventHandler;
}

bool wxCmdlineProtocol::Close()
{
    m_pCmdlineProtocolSocketEventHandler->incserial();
    return wxSocketClient::Close();
}

bool wxCmdlineProtocol::Connect()
{
	bool bRes(true);

	if (IsConnected()) Close();

	//
	// I'm paranoid that we might not have called Close() in
	// some uncommon scenario where, for example, the server
	// goes away suddenly, so I'm not confident incserial()
	// got called between retries. It's OK to call it here
	// again.
	//
	m_pCmdlineProtocolSocketEventHandler->incserial();
	SetClientData((void *)m_pCmdlineProtocolSocketEventHandler->
	    getserial());

	SetTimeout(20);
	SetEventHandler(*m_pCmdlineProtocolSocketEventHandler, SOCKET_ID);
	SetNotify(
	    wxSOCKET_CONNECTION_FLAG |
	    wxSOCKET_INPUT_FLAG |
	    wxSOCKET_LOST_FLAG);
	Notify(TRUE);

	// connect
	wxIPV4address addr;
	addr.Hostname(m_host);
	if (m_port) {
	    addr.Service(m_port);
	} else {
	    addr.Service(m_service);
	}

	if ( !wxSocketClient::Connect(addr, true) ||
		 !WaitOnConnect(-1, 500) )
	{

	    //
	    // generate a socket lost event
	    //
	    wxSocketEvent ev(SOCKET_ID);
	    ev.m_event	= wxSOCKET_LOST;
	    ev.m_clientData = (void *)m_pCmdlineProtocolSocketEventHandler->
		getserial();
	    ev.SetEventObject(this);
	    m_pCmdlineProtocolSocketEventHandler->AddPendingEvent(ev);

		bRes = false;
	}

	return bRes;
}

void wxCmdlineProtocol::OnInput(wxSocketEvent& event)
{
	try
	{
		const int bufsize = 4096;
		char buf[bufsize];

		memset( buf,0, sizeof(buf) );
		Read(buf, bufsize);

		stringstream sIn( buf );
		queue<string> qS;

		char bufLine[512];
		while ( !sIn.getline(bufLine, sizeof(bufLine)).eof() )
		{
			wxString strLine = wxString::FromAscii(bufLine);

			#if defined(_DEBUG) && defined(WIN32)
			//OutputDebugStr(_T("\n-----------------------------\n"));
			//OutputDebugStr(strLine);
			//OutputDebugStr(_T("\n-----------------------------\n"));
			#endif

			long lOpcode = atol( bufLine );

			if (!strLine.empty() &&
				lOpcode>=200 &&
				lOpcode<=554 )
			{
				qS.push(bufLine);
			}
		}

		if ( !qS.empty() )
		{
			wxString strLine = wxString::FromAscii( qS.back().c_str() );

			#if defined(_DEBUG) && defined(WIN32)
			OutputDebugStr(_T("\nRecv-----------------------------\n"));
			OutputDebugStr(strLine);
			OutputDebugStr(_T("\n-----------------------------\n"));
			#endif

			EvaluateLine(strLine);
		}
	}
	catch(...)
	{
	}
}

void wxCmdlineProtocol::OnSocketEvent(wxSocketEvent& event)
{
	wxString s = _("OnSocketEvent: ");


	switch(event.GetSocketEvent())
	{
		case wxSOCKET_INPUT:
			OnInput(event);
			break;
		case wxSOCKET_LOST:
			OnConnectionLost(event);
			break;
		case wxSOCKET_CONNECTION:
			OnConnect(event);
			break;
                default:
                        break;
	}
}

void wxCmdlineProtocol::Write(const wxString& msg)
{
#ifdef COUT_DEBUG
    cout << ">>" << msg << "<<" << endl;
#endif

#if defined(_DEBUG) && defined(WIN32)
	OutputDebugStr(_T("\nmsg--------------------------\n"));
	OutputDebugStr(msg);
	OutputDebugStr(_T("\n-----------------------------\n"));
#endif

	wxSocketClient::Write( msg.ToAscii(), msg.Length());
}

